package ink.metoo.gude.module.websocket.service.impl

import ink.metoo.gude.model.TopicConst
import ink.metoo.gude.module.security.domain.SimpleUserDetails
import ink.metoo.gude.module.security.util.original
import ink.metoo.gude.module.security.util.simpleUserDetails
import ink.metoo.gude.module.websocket.domain.ToPublicType
import ink.metoo.gude.module.websocket.domain.UserChangeStatus
import ink.metoo.gude.module.websocket.domain.vo.PrivateConnectStatisticsVO
import ink.metoo.gude.module.websocket.domain.vo.PublicUserStatusChangeVO
import ink.metoo.gude.module.websocket.service.SessionNoticeService
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.messaging.simp.user.SimpUserRegistry
import org.springframework.security.authentication.AnonymousAuthenticationToken
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.stereotype.Service

@Service
class SessionNoticeServiceImpl(
    private val messageTemplate: SimpMessagingTemplate,
    private val simpUserRegistry: SimpUserRegistry
) : SessionNoticeService {

    override fun sendConnectStatistics(sessionId: String, userDetails: SimpleUserDetails?) {
        val body = PrivateConnectStatisticsVO()
        val userMap = hashMapOf<Long?, PrivateConnectStatisticsVO.InnerUser>()
        var anonymousCount = 0
        simpUserRegistry.users.forEach {
            when (val principal = it.principal) {
                is UsernamePasswordAuthenticationToken -> {
                    val original = principal.simpleUserDetails.original ?: return@forEach
                    userMap.compute(original.id) { _, value ->
                        value?.copy(connect = value.connect + 1)
                            ?: PrivateConnectStatisticsVO.InnerUser(
                                id = original.id,
                                account = original.account,
                                nickname = original.nickname,
                                email = original.email,
                                avatar = original.avatar,
                                connect = 1
                            )
                    }
                }

                is AnonymousAuthenticationToken -> {
                    anonymousCount++
                }
            }
        }

        body.online = userMap.size + anonymousCount
        body.anonymous = anonymousCount
        body.users = userMap.values.toList()
        messageTemplate.convertAndSendToUser(sessionId, TopicConst.PRIVATE_TOPIC, body)
    }

    override fun sendUserStatusChangeNotice(
        sessionId: String,
        userDetails: SimpleUserDetails?,
        type: ToPublicType,
        status: UserChangeStatus
    ) {
        if (userDetails == null) {
            val body = PublicUserStatusChangeVO(status, true)
            body.sessionId = sessionId
            messageTemplate.convertAndSend(TopicConst.PUBLIC_TOPIC, body)
            return
        }
        val findU = simpUserRegistry.users.findLast {
            it.principal
                .simpleUserDetails?.username == userDetails.username
        }

        when (status) {
            UserChangeStatus.JOIN -> {
                findU ?: return
            }

            UserChangeStatus.LEAVE -> {
                if (findU != null) return
            }
        }

        val body = PublicUserStatusChangeVO(status)
        val original = userDetails.user
        body.sessionId = sessionId
        body.id = original.id
        body.account = original.account
        body.nickname = original.nickname
        body.email = original.email
        body.gravatar = original.avatar
        messageTemplate.convertAndSend(TopicConst.PUBLIC_TOPIC, body)
    }

}